﻿using System;
using System.Collections.Generic;
using System.Transactions;
using System.Web;
using System.Web.Security;
using TSharp.Core.Mvc.Pager;
using TSharp.Core.Service.Impl;
using TSharp.Core.Util;
using TSharp.Core.Service.Constant;

namespace TSharp.Core.Service
{

    class MembershipService : IMembershipService
    {
        private readonly MembershipProvider _provider;
        private readonly IUserExtendService _userService;
        private readonly ITokenService _tokenService;

        [Microsoft.Practices.Unity.InjectionConstructor]
        public MembershipService(IUserExtendService userService, ITokenService tokenService)
            : this((MembershipProvider)null)
        {
            _userService = userService;
            _tokenService = tokenService;
        }

        public MembershipService(MembershipProvider provider)
        {
            _provider = provider ?? Membership.Provider;
        }

        public int MaxInvalidPasswordAttempts
        {
            get { return _provider.MaxInvalidPasswordAttempts; }
        }

        public int MinPasswordLength
        {
            get
            {
                return _provider.MinRequiredPasswordLength;
            }
        }

        public bool ValidateUser(HttpContextBase context, string userName, string password)
        {
            ValidationUtil.ValidateRequiredStringValue(userName, "key");
            ValidationUtil.ValidateRequiredStringValue(password, "password");
            if (_provider.ValidateUser(userName, password))
                return true;
            MembershipUser muser = _provider.GetUser(userName, false);
            if (muser == null)
            {
                throw new AccountException(String.Format("用户'{0}'登录失败，该用户不存在。", userName), AccountException.ErrorType.NoUser);
            }
            if (!muser.IsApproved)
            {
                throw new AccountException(String.Format("用户'{0}'登录失败，该用户被禁用。", userName), AccountException.ErrorType.NoApproved);
            }
            if (muser.IsLockedOut)
            {
                if (!TryUnlockUserSeccuss(muser))
                {
                    throw new AccountException(string.Format("用户'{0}'因密码错误次数超限，已被锁定。请过段时间再尝试登录。", userName), AccountException.ErrorType.LockedOut);
                }
                else if (_provider.ValidateUser(userName, password))
                    return true;
            }
            throw new AccountException(string.Format("用户'{0}'密码错误。", userName, MaxInvalidPasswordAttempts), AccountException.ErrorType.ErrorPassWord);
        }


        /// <summary>
        /// 用户自动解锁时间（分钟数）
        /// </summary>
        private static double AutoUnlockTimeout = 25;
        /// <summary>
        /// 尝试解锁用户
        /// <para>by tangjingbo at 2010/7/6 23:39</para>
        /// </summary>
        /// <param name="mUser">The m user.</param>
        /// <returns>返回解锁是否成功</returns>
        private bool TryUnlockUserSeccuss(MembershipUser mUser)
        {
            bool success = false;
            if (mUser == null)
                success = false;
            if (!mUser.IsLockedOut)
            {
                success = true;
            }
            if ((mUser.LastLockoutDate.ToUniversalTime().AddMinutes(AutoUnlockTimeout) < DateTime.UtcNow))
                success = mUser.UnlockUser();
            else
                success = false;
            return success;
        }

        public MembershipCreateStatus CreateUser(string userName, string password, string email)
        {
            ValidationUtil.ValidateRequiredStringValue(userName, "key");
            ValidationUtil.ValidateRequiredStringValue(password, "password");
            ValidationUtil.ValidateRequiredStringValue(email, "email");
            return CreateUser(userName, password, email, null, null, true, null);
        }

        public MembershipCreateStatus CreateUser(string userName,
                                                   string password,
                                                   string email,
                                                   string passwordQuestion,
                                                   string passwordAnswer,
                                                   bool isApproved,
                                                   object providerUserKey
                                                   )
        {
            ValidationUtil.ValidateRequiredStringValue(userName, "key");
            ValidationUtil.ValidateRequiredStringValue(password, "password");
            ValidationUtil.ValidateRequiredStringValue(email, "email");
            MembershipCreateStatus status;
            _provider.CreateUser(userName, password, email, passwordQuestion, passwordAnswer, isApproved, providerUserKey, out status);
            return status;
        }

        public bool ChangePassword(string userName, string oldPassword, string newPassword)
        {
            ValidationUtil.ValidateRequiredStringValue(userName, "key");
            ValidationUtil.ValidateRequiredStringValue(oldPassword, "oldPassword");
            ValidationUtil.ValidateRequiredStringValue(newPassword, "newPassword");
            // The underlying ChangePassword() will throw an exception rather
            // than return false in certain failure scenarios.
            try
            {
                MembershipUser currentUser = _provider.GetUser(userName, true /* userIsOnline */);
                return currentUser.ChangePassword(oldPassword, newPassword);
            }
            catch (ArgumentException)
            {
                return false;
            }
            catch (MembershipPasswordException)
            {
                return false;
            }
        }

        #region  密码找回

        public bool ResetPassword(ParameterizedUri url, string newPassword)
        {
            if (ValidateResetPasswordUrl(url))
            {
                var userName = url.Queries[MembershipServiceConst.URLKEY_USERNAME];
                var tmppwd = _provider.GetUser(userName, false).ResetPassword();
                return _provider.ChangePassword(userName, tmppwd, newPassword);
            }
            return false;
        }
        #endregion

        public IUser GetUser(HttpContextBase context, string username, bool userIsOnline)
        {
            var mUser = _provider.GetUser(username, userIsOnline);
            var user = ConvertToUser(context, mUser);
            return user;
        }

        public List<IUser> GetUsers(HttpContextBase context, int pageIndex, int pageSize, out int totalRecords)
        {
            var list = _provider.GetAllUsers(pageIndex, pageSize, out totalRecords);
            return ConvertAll(context, list);
        }

        #region IUserService Members

        public PagedList<IUser> FindAll(HttpContextBase context, int pageIndex, int pageSize)
        {
            // get one page of users
            int totalUserCount;
            var usersCollection = _provider.GetAllUsers(pageIndex, pageSize, out totalUserCount);

            var usersList = ConvertAll(context, usersCollection);
            var usersPagedList = new PagedList<IUser>(usersList, pageIndex, pageSize, totalUserCount);
            return usersPagedList;
        }

        public PagedList<IUser> FindByEmail(HttpContextBase context, string emailAddressToMatch, int pageIndex, int pageSize)
        {
            // get one page of users
            int totalUserCount;
            var usersCollection = _provider.FindUsersByEmail(emailAddressToMatch, pageIndex, pageSize, out totalUserCount);

            var usersList = ConvertAll(context, usersCollection);
            var usersPagedList = new PagedList<IUser>(usersList, pageIndex + 1, pageSize, totalUserCount);
            return usersPagedList;
        }

        public PagedList<IUser> FindByUserName(HttpContextBase context, string userNameToMatch, int pageIndex, int pageSize)
        {
            // get one page of users
            int totalUserCount;
            var usersCollection = _provider.FindUsersByName(userNameToMatch, pageIndex, pageSize, out totalUserCount);

            var usersList = ConvertAll(context, usersCollection);
            var usersPagedList = new PagedList<IUser>(usersList, pageIndex + 1, pageSize, totalUserCount);
            return usersPagedList;
        }



        public IUser Get(HttpContextBase context, object providerUserKey)
        {
            var muser = _provider.GetUser(providerUserKey, false);
            var user = ConvertToUser(context, muser);
            return user;
        }



        public void Delete(HttpContextBase context, IUser user)
        {
            _provider.DeleteUser(user.UserName, false);
        }

        public void Delete(HttpContextBase context, IUser user, bool deleteAllRelatedData)
        {
            _provider.DeleteUser(user.UserName, deleteAllRelatedData);
        }

        public IUser Touch(HttpContextBase context, IUser user)
        {
            return GetUser(context, user.UserName, true);
        }

        public IUser Touch(HttpContextBase context, string userName)
        {
            return GetUser(context, userName, true);
        }

        public IUser Touch(HttpContextBase context, object providerUserKey)
        {
            var muser = _provider.GetUser(providerUserKey, true);
            return ConvertToUser(context, muser);
        }

        public int GetTotalUsers()
        {
            int totalUsers;
            _provider.GetAllUsers(1, 1, out totalUsers);
            return totalUsers;
        }

        public int UsersOnline
        {
            get
            {
                return _provider.GetNumberOfUsersOnline();
            }
        }

        #endregion

        private IUser ConvertToUser(HttpContextBase context, MembershipUser mUser)
        {
            IUser user = (User)mUser;
            user = _userService.GetExtend(context, ref user);
            return user;
        }
        private List<IUser> ConvertAll(HttpContextBase context, MembershipUserCollection users)
        {
            List<IUser> list = new List<IUser>(users.Count);
            foreach (var user in users)
            {
                list.Add(ConvertToUser(context, (MembershipUser)user));
            }
            return list;
        }

        public bool DeleteUser(HttpContextBase context, string userName)
        {
            using (var ts = new TransactionScope())
            {
                try
                {
                    var deleteAllRelatedData = true;
                    if (!string.IsNullOrEmpty(userName))
                        if (_provider.DeleteUser(userName, deleteAllRelatedData))
                        {
                            _userService.DeleteExtend(context, userName);
                        }
                    ts.Complete();
                    return true;
                }
                catch
                {
                    throw;
                }
            }
        }

        public bool DeleteUser(HttpContextBase context, IUser user)
        {
            return DeleteUser(context, user.UserName);
        }

        public bool Update(HttpContextBase context, IUser user)
        {
            using (var ts = new TransactionScope())
            {
                try
                {
                    _provider.UpdateUser((User)user);
                    _userService.DeleteExtend(context, user);
                    ts.Complete();
                    return true;
                }
                catch
                {
                    throw;
                }
            }
        }

        public bool UnLockUser(string username)
        {
            return _provider.UnlockUser(username);
        }

        public bool SetApproved(HttpContextBase context, IUser user)
        {
            user.IsApproved = true;
            return Update(context, user);
        }

        public int GetNumberOfUsersOnline()
        {
            return _provider.GetNumberOfUsersOnline();
        }

       
        public ParameterizedUri GetResetPasswordUrl(string userName)
        {
            var st = DateTime.UtcNow;
            var et = st.AddHours(MembershipServiceConst.InvalidHour);
            var uri = new ParameterizedUri("~/account/forgetpwd");
            uri.SetQuery(MembershipServiceConst.URLKEY_USERNAME, userName)
            .SetQuery(MembershipServiceConst.URLKEY_CUSTOMINFO, et.ToLocalTime().ToString("有效期截至时间：yyyy-MM-dd HH:mm:ss"));
            return _tokenService.GetTokenUrl(uri, st, et);
        }

        public bool ValidateResetPasswordUrl(ParameterizedUri url)
        {
            return _tokenService.ValidateTokenUrl(url, DateTime.UtcNow);
        }

    }
}